/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ia_multiagent1;

import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Harakan
 */
public class Agent {

    private int x;
    private int y;
    private Vector<LogAgent> histoireAgent;


    private int spaceSizeX;
    private int spaceSizeY;
    private Espace2D espace2D;
    private int bucketR1;
    private int deplacements;
    private int bucketR2;
    private int bucketR3;
    private Comportement comportement;
    private int ressourceTransportee; // Même convention que pour les cases
  
    private Vector <Pheromone> pheromones;
    private int dureeViePheromone;
    private boolean disparitionPheromone;
    private boolean priseEncomptePheromones;

        public Vector<LogAgent> getHistoireAgent() {
        return histoireAgent;
    }

    public Agent(int x, int y, int spaceSize, Espace2D e2d, int dureeViePheromone,
                 boolean disparitionPheromone, boolean priseEncomptePheromones ) {
        this.x = x;
        this.y = y;
        this.comportement = Comportement.erratique;
        this.ressourceTransportee = 0;
        histoireAgent = new Vector();
        histoireAgent.add(new LogAgent(0, x, y, 0)); //initialisation du vecteur historique au temps 0...
        this.spaceSizeX = spaceSize;
        this.spaceSizeY = spaceSize;
        this.espace2D = e2d;
        this.pheromones = new Vector <Pheromone>();
        this.dureeViePheromone = dureeViePheromone;
        this.disparitionPheromone = disparitionPheromone;
        this.priseEncomptePheromones = priseEncomptePheromones;
    }
    
    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }
    
    public int getBucketR1() {
        return bucketR1;
    }

    public int getBucketR2() {
        return bucketR2;
    }

    public int getBucketR3() {
        return bucketR3;
    }
    
    //fonction mouvement d'un agent
    
    
    public int getMouvementX(int x,int spaceSizeX){
        int dx = (int) Math.floor(3*Math.random()-1);
        if (x <= 0 && dx == -1){ 
            dx = 0;
        }        
        if (x >= spaceSizeX-1 && dx == 1){
            dx = 0;
        }
        return dx;
    }
    
    
    public int getMouvementY(int y,int spaceSizeY){
        int dy = (int) Math.floor(3*Math.random()-1);
        if (y <= 0 && dy == -1){ 
             dy = 0;
        }
        if (y >= spaceSizeY-1 && dy == 1){
            dy = 0;
         }
        return dy;
    }
    
    public void moveAleatoire(){
        x += getMouvementX(x,spaceSizeX);
        y += getMouvementY(y,spaceSizeY);
        deplacements++;
        switch (this.espace2D.getCase(x, y).getRessource()){
            case 1: 
                bucketR1++;
                break;
            case 2: 
                bucketR2++;
                break;
            case 3: 
                bucketR3++;
                break;
        }
        
        
        if (priseEncomptePheromones){
            this.priseEnComptePheromone();
        }
        
    }
    
    // on effectue le mouvement en fonction 
    @SuppressWarnings("empty-statement")
    public void moveAttraction() throws Exception{        
        /*
        int min = this.getMinimumAttractionCasesVoisines();
        int sommeMin = this.sommeWithMinAttractionCasesVoisines(min);

        System.out.println("somme = " + sommeMin);
        System.out.println("min = " + min);
        */
        
        Vector<Case> mesCasesVoisines = getCasesVoisines();
        
        double [] poidsRelatifs = new double[mesCasesVoisines.size()];
        double [] frequence = new double[mesCasesVoisines.size()] ; //ce vecteur stock les intervalles pour le tirage aleatoire qui décidera quelle case chosir
 
     
        if (espace2D.getMesPointsAttraction().size()==0){
            
            System.out.println("ATTENTION : Aucun point d'attraction n'a ete definit alors que notre agent devrait bouger en fonctiond 'eux ====> mouveemnt aleatoire declenche");
            this.moveAleatoire();
            // throw new Exception("N'oubliez pas d'initialiser les points d'attraction... !!!");                 
        }
        else{
       // System.out.println("taille du vecteur de points attraction : "+espace2D.getMesPointsAttraction().size()); 
        
         
        double somme = 0.0;
       
        for (int i=0;i<mesCasesVoisines.size();i++){    
            double mu = 0;  //on réinitialise le mu pour la case i en cours de traitement 
            int cx = mesCasesVoisines.elementAt(i).getX();
            int cy = mesCasesVoisines.elementAt(i).getY();

         for (int iPA = 0;iPA< espace2D.getMesPointsAttraction().size();iPA++){
             int ox = espace2D.getMesPointsAttraction().elementAt(iPA).getX();
             int oy = espace2D.getMesPointsAttraction().elementAt(iPA).getY(); 
             int portee = espace2D.getMesPointsAttraction().elementAt(iPA).getPortee();
             double m = (double) espace2D.getMesPointsAttraction().elementAt(iPA).getIntensite();                         
                   
            double distance  = Math.sqrt((double) (ox - cx)*(ox - cx) + (oy - cy)*(oy - cy));
            m = (double) espace2D.getMesPointsAttraction().elementAt(0).getIntensite();
        
            double poidsPotentiel = m*(1-distance/portee);
            mu = Math.max(0.0,poidsPotentiel) + mu; //on ajoute le mu précédemment cazlculé, du aux (iPA-1) Points attracteurs 
            
            
         }
            poidsRelatifs[i] = mu+ 0.125; //la case courante <ic,jc> est pondere par 1/8 + mu(i,j)  
            somme = somme + poidsRelatifs[i];
        }
        
         frequence[0] = (0.125 + poidsRelatifs[0])/(somme+1);
        // System.out.println("frequence[0] = " + frequence[0]);
        for (int i=1;i<mesCasesVoisines.size();i++){   
            
            frequence[i] = (0.125 + poidsRelatifs[i])/(somme+1) + frequence[i-1];   //on ajoute la frequence precedente pour avoir un tableau d'intervalle de frequence.   
          //  System.out.println("frequence[" + i + "] = " + frequence[i]);            
        }
   
       /* System.out.println("poidsRelatifs[" + 0 + "] = " + poidsRelatifs[0]);    
                 for (int i=1;i<mesCasesVoisines.size();i++){   
                      System.out.println("poidsRelatifs[" + i + "] = " + poidsRelatifs[i]); 
                 }
       */
         
       /*  System.out.println("proba[" + 0 + "] = " + frequence[0]);    
                 for (int i=1;i<mesCasesVoisines.size();i++){   
                      System.out.println("proba[" + i + "] = " + (frequence[i]-frequence[i-1])); 
                 }
         
        */
        if (frequence[1] == 0 || (frequence[1] == 0)){
                 throw new Exception("problem. probablement sur le cast double, cas impossible, solo possible si arrondis");   
        }
        if (frequence[mesCasesVoisines.size()-1] == 0 || (frequence[1] == 0)){
                 throw new Exception("problem. probablement sur le cast double, cas impossible, solo possible si arrondis");   
        }       
        
        double d = Math.random();
        int j = 0;
  
              //TEST champs    
        //System.out.println("d = " + d);
        //System.out.println("mesCasesVoisines.size() = " + mesCasesVoisines.size());
         
        int comparateur = Double.compare(d,frequence[j]);
        j++;
        while (comparateur>=0 && j<mesCasesVoisines.size()){
            
            comparateur = Double.compare(d,frequence[j]);
            j++;
        }
        j = j-1; //en cas de sortie de boucle, le j a quand mêem été incremente.
       //  System.out.println("j retenu = " + j);
       
       if (j == mesCasesVoisines.size() || j<0){
                 System.err.println("ERROR j retenu = " + j);
                 throw new Exception("problem probable ici");   
        }
        
        x = mesCasesVoisines.elementAt(j).getX();
        y = mesCasesVoisines.elementAt(j).getY();
        deplacements++;
        
        this.priseEnComptePheromone();
    }
    }
    
    
    
    /* OLD
    public int sommeAttractionCasesVoisines(){
        int res = 0;
        Vector<Case> mesCasesVoisines = getCasesVoisines();
        for (int i=0;i<mesCasesVoisines.size();i++){
            res = res + mesCasesVoisines.elementAt(i).getAttraction();
        }        
        return res;
    }
    
    private int sommeWithMinAttractionCasesVoisines(int min) {
         int res = 0;
        Vector<Case> mesCasesVoisines = getCasesVoisines();
        for (int i=0;i<mesCasesVoisines.size();i++){
            res = res + mesCasesVoisines.elementAt(i).getAttraction() - min +1; //le plus un est une compensation du zero possible 
        }        
        return res;
    }
    
   public int getMinimumAttractionCasesVoisines(){
        
        Vector<Case> mesCasesVoisines = getCasesVoisines();
        int res = mesCasesVoisines.elementAt(0).getAttraction();
        for (int i=1;i<mesCasesVoisines.size();i++){
            if (mesCasesVoisines.elementAt(i).getAttraction()<res){
                res = mesCasesVoisines.elementAt(i).getAttraction();
            }
        }        
        return res;
    } 
    
    */
    
    
    public Vector<Case> getCasesVoisines(){
        Vector <Case> res = new Vector <Case> () ;      
        if (x == 0) { //on supose implicitement spaceSizeX>1. on est sur la première colonne
            res.add(espace2D.getCase(x+1,y));
            if (y ==0 ){ //on supose implicitement spaceSizeY>1
                res.add(espace2D.getCase(x,y+1));
                res.add(espace2D.getCase(x+1,y+1));
                                    // 3 cases ajoutées en tout
            }
            else{ //y != 0
                if (y == spaceSizeY -1 ){ //on supose implicitement spaceSizeY>1
                    res.add(espace2D.getCase(x,y-1));
                    res.add(espace2D.getCase(x+1,y-1));
                                    // 3 cases ajoutées en tout
                }
                else{//y != spaceSizeY -1 et y != 0 --> on est au milieur d'un côté
                    res.add(espace2D.getCase(x,y+1));
                    res.add(espace2D.getCase(x+1,y+1));                    
                    res.add(espace2D.getCase(x,y-1));
                    res.add(espace2D.getCase(x+1,y-1));
                                    // 5 cases ajoutées en tout
                }
            }              
        }       
        else{ // x != 0 
            if (x == spaceSizeX -1 ){ //on est sur la dernière colonne
                  res.add(espace2D.getCase(x-1,y));
                  if (y ==0 ){ //on supose implicitement spaceSizeY>1
                      res.add(espace2D.getCase(x,y+1));
                      res.add(espace2D.getCase(x-1,y+1));
                                    // 3 cases ajoutées en tout
                  }
                   else{ //y != 0
                        if (y == spaceSizeY -1 ){ //on supose implicitement spaceSizeY>1
                                res.add(espace2D.getCase(x,y-1));
                                res.add(espace2D.getCase(x-1,y-1));
                                    // 3 cases ajoutées en tout
                         }
                          else{//y != spaceSizeY -1 et y != 0 --> on est au milieur d'un côté
                             res.add(espace2D.getCase(x,y+1));
                             res.add(espace2D.getCase(x-1,y+1));                    
                             res.add(espace2D.getCase(x,y-1));
                             res.add(espace2D.getCase(x-1,y-1));
                                    // 5 cases ajoutées en tout
                            }
                    }     
              }
            else{ //x != 0 et x != spaceSizeX -1 ("murs droite et gauche traites")
                res.add(espace2D.getCase(x-1,y));
                res.add(espace2D.getCase(x+1,y));
                if (y ==0 ){ //on supose implicitement spaceSizeY>1
                      res.add(espace2D.getCase(x,y+1));
                      res.add(espace2D.getCase(x-1,y+1));
                      res.add(espace2D.getCase(x+1,y+1));                     
                                    // 5 cases ajoutées en tout
                  }
                  else{ //y != 0
                        if (y == spaceSizeY -1 ){ //on supose implicitement spaceSizeY>1
                                res.add(espace2D.getCase(x,y-1));
                                res.add(espace2D.getCase(x-1,y-1));
                                res.add(espace2D.getCase(x+1,y-1));                                                                
                                    // 5 cases ajoutées en tout
                         }
                          else{//y != spaceSizeY -1 et y != 0 --> on est sur aucun bord
                             res.add(espace2D.getCase(x,y+1));
                             res.add(espace2D.getCase(x-1,y+1));
                             res.add(espace2D.getCase(x+1,y+1));
                             res.add(espace2D.getCase(x,y-1));
                             res.add(espace2D.getCase(x-1,y-1));
                             res.add(espace2D.getCase(x+1,y-1));
                                    // 8 cases ajoutées en tout
                            }
                    }                 
             }
        }                  
        return res ;
    }
    
    
    public boolean priseRessource(){
        int ressource = this.espace2D.getCase(x, y).getRessource();
        if (ressource == 0 || ressourceTransportee != 0){
            return false;
        }
        else{
            // On lance un "dé" à 8 faces : si le score est supérieur à la fréquence,
            // on prend la ressource
            int freq = this.capteur(ressource);
            int dice = (int) Math.floor(8*Math.random()+1);
            if (dice>freq){
                this.ressourceTransportee = ressource;
                this.espace2D.getCase(x, y).setRessource(0);
                //System.out.println("Agent prend la ressource "+ressource);
                return true;
            }
            else{
                return false;
            }
        }
    }
    
    public boolean poseRessource(){
        int ressource = this.espace2D.getCase(x, y).getRessource();
        if (ressourceTransportee == 0 || ressource != 0){
            return false;
        }
        else{
            // On lance un dé à 8 faces : si le score est inférieur ou egal à la fréquence,
            // on prend la ressource
            int freq = this.capteur(ressourceTransportee);
            int dice = (int) Math.floor(8*Math.random()+1);
            if (dice<=freq){
                //System.out.println("Agent pose la ressource "+ressourceTransportee+" ( dice = "+dice+"; freq = "+freq+")");
                this.espace2D.getCase(x, y).setRessource(ressourceTransportee);
                this.ressourceTransportee = 0;
                return true;
            }
            else{
                return false;
            }
        }
    }

    public void updateLog(int t) {
        if (deplacements<2000){
            LogAgent monLogAgent = new LogAgent(t, x, y, ressourceTransportee);
            histoireAgent.add(monLogAgent);
        }
    }
    
    public String getHistoire(){
        String res = "Matrice de l'histoire = \n ";
        for (int i = 0;i<histoireAgent.size();i++){
           res += histoireAgent.elementAt(i).getStringLine() + "\n";
        }
        return res;
    }
    
    public int getXPrecedent() throws HistoriqueException{
        if (this.histoireAgent.size() == 1){
            throw new HistoriqueException("L'agent ne s'est pas encore deplace.");
        }
        else{
            return this.histoireAgent.lastElement().getX();
        }
    }
    
    public int getYPrecedent() throws HistoriqueException{
        if (this.histoireAgent.size() == 1){
            throw new HistoriqueException("L'agent ne s'est pas encore deplace.");
        }
        else{
            return this.histoireAgent.lastElement().getY();
        }
    }
    
    public Vector<LogAgent> getHistorique(){
        return this.histoireAgent;
    }
    
    public int capteur(int ressourceType){
        int bucket = 0;
        for (int di=-1; di<2; di++){
            for (int dj=-1; dj<2; dj++){
                if (!((di==-1 && this.x==0)||(di==1 && this.x==spaceSizeX-1)||(dj==-1 && this.y ==0)||(dj==1 && this.y ==spaceSizeX-1)||(di==0 && dj==0)) && (this.espace2D.getCase(this.x+di, this.y+dj).getRessource()==ressourceType)){
                    bucket++;
                }
            }
        }
        return bucket;
    }
    
    public void stepPheromone(){    
        int tailleCourante  = pheromones.size();
        int i=0;
        while(i<tailleCourante){
            if (pheromones.elementAt(i).getTimeRemainAlive() == 1){
                int pi = pheromones.elementAt(i).getX();
                int pj = pheromones.elementAt(i).getY();
                try {
                    espace2D.getCase(pi, pj).decreaseQtePheromone();
                } catch (Exception ex) {
                    Logger.getLogger(Agent.class.getName()).log(Level.SEVERE, null, ex);
                }
                pheromones.removeElementAt(i);  
                tailleCourante--; //le i reste identique, mais on diminue la taile du evcteur... le ieme element a été remplace par l'ancien i+1eme
            }
            else{
                try {
                    pheromones.elementAt(i).decreaseTimeRemainAlive();
                } catch (Exception ex) {
                    Logger.getLogger(Agent.class.getName()).log(Level.SEVERE, null, ex);
                }
                i++;
            }
        }
        //System.out.println("taille new vect = "+ tailleCourante);
        //System.out.println("taille new vect re = "+ pheromones.size());
        //System.out.println("vecteur pheromone agent = " + convertStringForLiveRemaining());      
    }

    public Vector<Pheromone> getPheromones() {
        return pheromones;
    }
    
    public String convertStringForLiveRemaining() {
        String res = "taille : "+pheromones.size()+" ,Vector : ";
        for(int i=0;i<pheromones.size();i++){
            res = res + " " + pheromones.elementAt(i).getTimeRemainAlive();
        }
        return res;
    }
        
    public void deposePheromone(int viePheromone){       
        this.pheromones.add(new Pheromone(x, y, viePheromone));
        espace2D.getCase(x, y).increaseQtePheromone();
        
    }   
    
    public void setDisparitionPheromone(boolean disparitionPheromone) {
        this.disparitionPheromone = disparitionPheromone;
        
    }
    
    public void priseEnComptePheromone(){      
        if (disparitionPheromone){
                this.stepPheromone();
        }
                this.deposePheromone(dureeViePheromone);         
    }
    
    public int AverageTimeWithoutMeetingRessource(int typeRessource){
        switch(typeRessource){
            case 1 :
                return deplacements / (this.getBucketR1()+1);
            case 2:
                return deplacements / (this.getBucketR2()+1);
            case 3:
                return deplacements / (this.getBucketR3()+1);
            default:
                return -1; // error
        }
    }

    
  
    
}
